#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <dirent.h>
#include <unistd.h>
#include <vector>
#include <sstream>
#include <fstream>
#include <sys/io.h>
#include <sys/times.h>
#include <iomanip> // setw()
using namespace std;

#include "Core"
using namespace Eigen;

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/eigen.hpp>
using namespace cv;

#include "opencv2/ximgproc.hpp"
using namespace cv::ximgproc;

#include "segFlag.hpp"
#include "alexNetForMnist.hpp"

void CornerPointDetection_Demo(const cv::Mat &src, 
                               const std::vector<std::vector<cv::Point> > &regs);
void ApproximatePolygonal_Demo(const cv::Mat &src, 
	                           const std::vector<std::vector<cv::Point> > &regs);
void perspectiveTransform_Demo(const cv::Mat &src, const segfg::sARct &reg);
void affineTransform_Demo(const cv::Mat &src, const segfg::sARct &reg);
void affineTransform_Demo1(const cv::Mat &src, const segfg::sARct &reg, int idx);
Point getPointAffinedPos(const Point &src, const Point center, double angle);

int main(int argc, char** argv)
{
	cout << ">> ----" << "\n" << endl;
	// Create an AlexCNN model
	PiaAlexNet cNN;
	CreateAlexForMnist(cNN, "./ocr_python/storage_conv/mnistConvModel.npz");
//	cNN.Print();

	cv::Mat src = imread("./data/0_1217.jpg", IMREAD_GRAYSCALE);
//	rotate(src, src, ROTATE_180);
//	resize(src, src, Size(), 0.5, 0.5, INTER_LINEAR);
//	imwrite("./z.jpg", src);
//	std::vector<segfg::sARct> regs;
//	segfg::process(src, regs);
//	cout << ">> regs.size() = " << regs.size() << endl;
	
	std::vector<segfg::sAPosi> regs;
	segfg::segmentFlag(src, regs);
	for (int r = 0; r < (int)regs.size(); r++) {
		for (int i = 0; i < (int)regs[r].ocImgs.size(); i++) {
			vector<MatrixXd> eigenImgs(1);
			cv2eigen(regs[r].ocImgs[i], eigenImgs[0]);
			eigenImgs[0] /= 255.f;
//			cout << "------------predict sta-------------" << endl;
			VectorXd Output;
			cNN.RunImgs(eigenImgs, Output);
//			cout << "------------predict end-------------" << endl;
//			cout << std::fixed << std::setprecision(5) << Output << endl;
			double maxvla = 0.f; int maxidx;
			for (int j = 0; j < Output.rows(); j++) {
				if (Output(j, 0) > maxvla) {
					maxvla = Output(j, 0);
					maxidx = j;
				}
			}
			if (maxvla > 0.6f) 
				regs[r].ocNums[i] = maxidx;
			else
				regs[r].ocNums[i] = -1;
			if (0) {
				cout << regs[r].ocNums[i] << endl;
				imshow("win1", regs[r].ocImgs[i]);
				waitKey(0);
			}
		}
	}
	if (1) {
		cv::Mat dst;
		cvtColor(src, dst, COLOR_GRAY2BGR);
		for (int i = 0; i < (int)regs.size(); i++) {
			std::vector<std::vector<Point> > bd(1);
			bd[0] = regs[i].bounder;
			drawContours(dst, bd, 0, Scalar(255,0,255), 2, LINE_8);
/*			for (int j = 0; j < (int)regs[i].ocBounders.size(); j++) {
				Scalar color11 = Scalar((unsigned)theRNG() & 255, 
		                                    (unsigned)theRNG() & 255, 
		                                    (unsigned)theRNG() & 255);
				drawContours(dst, regs[i].ocBounders, j, color11, 2, LINE_8);
			}*/
			cv::Rect rct = boundingRect(regs[i].bounder);
			stringstream ss;
			for (int j = 0; j < (int)regs[i].ocImgs.size(); j++) {
				if (regs[i].ocNums[j] >= 0) {
					ss << regs[i].ocNums[j];
				}
				else
					ss << "*";
				cout << i << ", " << j << ", " << regs[i].ocNums[j] << endl;
			}
			cv::putText(dst, ss.str(), cv::Point(rct.x, rct.y), FONT_HERSHEY_SIMPLEX, 0.8, 
						Scalar(255, 0, 0), 2, 8);
		}
		imshow("win1", dst);
		waitKey(0);
	}
//	affineTransform_Demo(src, regs[0]);
//	for (int i = 0; i < (int)regs.size(); i++)
//		affineTransform_Demo1(src, regs[i], i);
//	perspectiveTransform_Demo(src, regs[0]);
//	CornerPointDetection_Demo(src, regs);
//	ApproximatePolygonal_Demo(src, regs);
	
	cout << "\n" << ">> ----" << endl;
	return 0;
}

void affineTransform_Demo(const cv::Mat &src, const segfg::sARct &reg)
{
	Rect curRct = boundingRect(reg.bounder);
	int nD = 4;
	Rect dilRct = curRct;
	dilRct.x -= nD;
	dilRct.y -= nD;
	dilRct.width += nD*2;
	dilRct.height += nD*2;
	Mat roi = src(dilRct);
	imwrite("./z0.jpg", roi);
	int si = (int)reg.bounder.size();
	std::vector<Point> crpVct(si);
	for (int i = 0; i < si; i++) {
		crpVct[i].x = reg.bounder[i].x - dilRct.x;
		crpVct[i].y = reg.bounder[i].y - dilRct.y;
	}
	
	RotatedRect box = minAreaRect(crpVct);
	double scale = 1.f;
	Mat rotMat = getRotationMatrix2D(box.center, box.angle, scale);
	Mat warped_image;
	warpAffine(roi, warped_image, rotMat, Size(dilRct.width, dilRct.height));
	
	Mat tmp;
	cvtColor(warped_image, tmp, COLOR_GRAY2BGR);
	for (int i = 0; i < crpVct.size(); i++) {
		Scalar color11 = Scalar((unsigned)theRNG() & 255, 
                                (unsigned)theRNG() & 255, 
                                (unsigned)theRNG() & 255);
		Point pt = getPointAffinedPos(crpVct[i], box.center, box.angle);
		circle(tmp, pt, 1, color11, 1, 8, 0);
	}
	imwrite("./z1.jpg", tmp);
}

void affineTransform_Demo1(const cv::Mat &src, const segfg::sARct &reg, int idx)
{
	Rect curRct = boundingRect(reg.bounder);
	int nD = 10;
	Rect dilRct = curRct;
	dilRct.x -= nD;
	dilRct.y -= nD;
	dilRct.width += nD*2;
	dilRct.height += nD*2;
	Mat roi = src(dilRct);

	int si = (int)reg.poly.size();
	std::vector<Point> crpVct(si);
	for (int i = 0; i < si; i++) {
		crpVct[i].x = reg.poly[i].x - dilRct.x;
		crpVct[i].y = reg.poly[i].y - dilRct.y;
	}
	// 设置源图像和目标图像上的三组点以计算仿射变换
	Point2f srcTri[3];
    Point2f dstTri[3];
	srcTri[0] = crpVct[0];
    srcTri[1] = crpVct[1];
    srcTri[2] = crpVct[2];
    dstTri[0] = Point2f(nD, nD);
    dstTri[1] = Point2f(nD, dilRct.height - nD);
    dstTri[2] = Point2f(dilRct.width - nD, dilRct.width - nD);
	Mat warp_mat = getAffineTransform( srcTri, dstTri);
	Mat tmp;
	warpAffine(roi, tmp, warp_mat, Size(dilRct.width, dilRct.height));
	
	for (int i = 0; i < (int)reg.oc.size(); i++) {
		si = reg.oc[i].size();
		std::vector <Point2f> crpBounder(si);
		for (int j =0; j < si; j++) {
			crpBounder[j].x = reg.oc[i][j].x - dilRct.x;
			crpBounder[j].y = reg.oc[i][j].y - dilRct.y;
		}
		cv::transform(crpBounder, crpBounder, warp_mat);
		Rect crpRct = boundingRect(crpBounder);
//		rectangle(abc, Point(crpRct.x, crpRct.y), Point(crpRct.x + crpRct.width,
//		          crpRct.y + crpRct.height), Scalar(255,0,255), 2);
		Mat curOC = tmp(crpRct);
		resize(curOC, curOC, Size(28, 28));
		cout << curOC.rows << ", " << curOC.cols << endl;
		stringstream ss;
		ss << "./z";
		ss << idx;
		ss << "_";
		ss << i;
		ss << ".jpg";
		threshold(curOC, curOC, 255, 255, THRESH_BINARY_INV | THRESH_OTSU);
		imwrite(ss.str(), curOC);
	}
}
 
Point getPointAffinedPos(const Point &src, const Point center, double angle)  
{  
    Point dst;  
    int x = src.x - center.x;  
    int y = src.y - center.y;  
  
    dst.x = (int)( x * cos(angle) + y * sin(angle) + center.x + 0.5);  
    dst.y = (int)(-x * sin(angle) + y * cos(angle) + center.y + 0.5);  
    return dst;  
}  

/*******************************************
* Performs the perspective matrix transformation of vectors
* see also  warpPerspective, getPerspectiveTransform, findHomography
*/
void perspectiveTransform_Demo(const cv::Mat &src, const segfg::sARct &reg)
{
	Rect curRct = boundingRect(reg.bounder);
	int nD = 4;
	Rect dilRct = curRct;
	dilRct.x -= nD;
	dilRct.y -= nD;
	dilRct.width += nD*2;
	dilRct.height += nD*2;
	Mat roi = src(dilRct);
	
	int si = (int)reg.bounder.size();
	std::vector<Point> crpVct(si);
	for (int i = 0; i < si; i++) {
		crpVct[i].x = reg.bounder[i].x - dilRct.x;
		crpVct[i].y = reg.bounder[i].y - dilRct.y;
	}
	
	RotatedRect box = minAreaRect(crpVct);
	Point2f vtx[4];
	box.points(vtx);
	std::vector<Point2f> c0(4), c1(4);
	c0[0] = vtx[0];
	c1[0] = Point2f(0,0);
	c0[1] = vtx[1];
	c1[1] = Point2f(box.size.width - 1, 0);
	c0[2] = vtx[2]; 
	c1[2] = Point2f(box.size.width - 1, box.size.height - 1);
	c0[3] = vtx[3];
	c1[3] = Point2f(0, box.size.height - 1);
	if (1) {
		Mat tmp;
		cvtColor(roi, tmp, COLOR_GRAY2BGR);
		for (int i = 0; i < 4; i++) {
			Scalar color11 = Scalar((unsigned)theRNG() & 255, 
                                    (unsigned)theRNG() & 255, 
                                    (unsigned)theRNG() & 255);
			circle(tmp, c0[i], 4, color11, -1, 8, 0);
			circle(tmp, c1[i], 4, Scalar(255,0,255), -1, 8, 0);
		}
		imwrite("./z0.jpg", tmp);
	}

	Mat transform = getPerspectiveTransform(c0, c1);
	Mat warped_image;
	warpPerspective(roi, warped_image, transform, Size(dilRct.width, dilRct.height));
	vector<Point2f> ptTrans;
	perspectiveTransform(c0, ptTrans, transform);
	if (1) {
		cout << ">> perspectiveTransform() OK" << endl;
		Mat tmp;
		cvtColor(warped_image, tmp, COLOR_GRAY2BGR);
		for (int i = 0; i < 4; i++) {
			Scalar color11 = Scalar((unsigned)theRNG() & 255, 
                                    (unsigned)theRNG() & 255, 
                                    (unsigned)theRNG() & 255);
			circle(tmp, ptTrans[i], 4, color11, -1, 8, 0);
		}
		rotate(tmp, tmp, ROTATE_180);
		imwrite("./z1.jpg", tmp);
	}
}

void CornerPointDetection_Demo(const cv::Mat &src, 
	                           const std::vector<std::vector<cv::Point> > &regs)
{
	for (int i = 0; i < (int)regs.size(); i++) {
		Rect curRct = boundingRect(regs[i]);
		Rect dilRct = curRct;
		dilRct.x -= 4;
		dilRct.y -= 4;
		dilRct.width += 8;
		dilRct.height += 8;
		cv::Mat mask(dilRct.height, dilRct.width, CV_8UC1, Scalar::all(0));
		drawContours(mask, regs, i, Scalar(255), 
					 CV_FILLED, LINE_8, cv::noArray(), INT_MAX,
					 Point(-dilRct.x, -dilRct.y));
		cv::Mat roi = src(dilRct);

		vector<Point2f> corners;
		segfg::CornerPointDetection(roi, mask, corners);
		if (false) {
			Mat tmp;
			cvtColor(roi, tmp, COLOR_GRAY2BGR);
			for (int j = 0; j < corners.size(); j++) {
				Scalar color11 = Scalar((unsigned)theRNG() & 255, 
                                        (unsigned)theRNG() & 255, 
                                        (unsigned)theRNG() & 255);
				circle(tmp, corners[j], 4, color11, -1, 8, 0);
            }
			
            imshow("win1", tmp);
            waitKey(0);
		}
	}
}

void ApproximatePolygonal_Demo(const cv::Mat &src, 
	                           const std::vector<std::vector<cv::Point> > &regs)
{
	for (int i = 0; i < (int)regs.size(); i++) {
		std::vector<Point> approxCurve;
		segfg::ApproximatePolygonal(regs[i], approxCurve);
		
		Mat tmp;
		cvtColor(src, tmp, COLOR_GRAY2BGR);
		for (int j = 0; j < approxCurve.size(); j++) {
			RNG rng(12345);  // random number generator
			int r = 4;
			circle(tmp, approxCurve[j], r, 
			       Scalar(rng.uniform(0, 255), rng.uniform(0, 255),
                   rng.uniform(0, 255)), 2, 8, 0);
        }
/*
        int size = (int)approxCurve.size();
            Point pt0 = approxCurve[size - 1];
            for (int j = 0; j < approxCurve.size(); j++) {
            	Point pt1 = approxCurve[j];
    			Scalar color11 = Scalar((unsigned)theRNG() & 255, 
                                        (unsigned)theRNG() & 255, 
                                        (unsigned)theRNG() & 255);
            	line(tmp, pt0, pt1, color11);
            	pt0 = pt1;
            }*/
            
		imshow("win1", tmp);
		waitKey(0);
	
	}
}








